package com.co.search.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.co.search.VideoInfo;
import com.co.search.mapper.VideoElasticSearchMapper;
import com.co.search.service.VideoService;
import com.co.video.feign.DetailFeign;
import com.co.video.feign.VideoFeign;
import com.co.video.feign.VideoStateFeign;
import com.co.video.pojo.Detail;
import com.co.video.pojo.VideoState;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ldl.plus
 * @date 2020年06月30日  15:39
 */
@Service
public class VideoServiceImpl implements VideoService {

    @Autowired
    private VideoFeign videoFeign;

    @Autowired
    private DetailFeign detailFeign;

    @Autowired
    private VideoElasticSearchMapper videoMapper;

    @Autowired
    private VideoStateFeign videoStateFeign;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 从数据库导入所有数据到es
     */
    @Override
    public void importData() {
        List<Detail> detailList = detailFeign.findAll().getData();
        List<VideoInfo> videoInfoList = detailList.stream().map(detail -> JSON.parseObject(JSON.toJSONString(detail), VideoInfo.class)).collect(Collectors.toList());
        for (VideoInfo videoInfo : videoInfoList) {
            VideoState videoState = videoStateFeign.findByAid(videoInfo.getAid()).getData();
            if (videoState == null) {
                continue;
            }
            videoInfo.setView(videoState.getView());
            videoInfo.setReply(videoState.getReply());
            videoInfo.setDanmaku(videoState.getDanmaku());
            videoInfo.setFavorite(videoState.getFavorite());
            videoInfo.setLike(videoState.getLike());
            videoInfo.setDislike(videoState.getDislike());
            videoInfo.setNowRank(videoState.getNowRank());
            videoInfo.setHisRank(videoState.getHisRank());
            videoInfo.setShare(videoState.getShare());
        }
        videoMapper.saveAll(videoInfoList);
    }

    @Override
    public void createIndex() {
        elasticsearchRestTemplate.createIndex(VideoInfo.class);
        elasticsearchRestTemplate.putMapping(VideoInfo.class);
    }

    /**
     * 从es中搜索给定条件的数据
     *
     * @param searchMap 搜索条件封装
     * @return 结果集合
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        // 结果封装map
        Map<String, Object> resultMap = new HashMap<>();

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        nativeSearchQueryBuilder.withQuery(boolQuery);

        /*if (searchMap == null || searchMap.size() == 0) {
            boolQuery.must(QueryBuilders.matchAllQuery());
            // 默认按照观看量降序排序
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("view").order(SortOrder.DESC));
            searchMap = new HashMap<>();
        } */
        // keyword: '',
        String keyword = searchMap.get("keyword");
        if (StringUtils.isNotEmpty(keyword)) {
            // 搜索关键字不为空，安装关键字搜索，搜索title与comment字段
            nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keyword).field("title").field(
                    "username").defaultOperator(Operator.OR));
        } else {
            // 为空则查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        // filter: '',
        String filter = searchMap.get("filter");
        if (StringUtils.isEmpty(filter)) {
            filter = "view";
        }
        switch (filter) {
            case "2":
                // 最多喜欢
                filter = "like";
                break;
            case "3":
                // 最新发布
                filter = "uploadTime";
                break;
            case "4":
                // 最多弹幕
                filter = "danmaku";
                break;
            case "5":
                // 最多收藏
                filter = "favorite";
                break;
            default:
                // 默认 综合排序
                filter = "view";
                break;
        }
        // 默认按照观看量降序排序
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(filter).order(SortOrder.DESC));
        // uploaddate: '',
        String uploadDate = searchMap.get("uploadDate");
        if (StringUtils.isEmpty(uploadDate)) {
            uploadDate = "";
        }
        switch (uploadDate) {
            case "2":
                // 今日
                uploadDate = "" + LocalDateTime.of(LocalDate.now(), LocalTime.MIN).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
                break;
            case "3":
                // 一周内
                uploadDate = "" + DateUtil.lastWeek().getTime();
                break;
            case "4":
                // 一月内
                uploadDate = "" + DateUtil.lastMonth().getTime();
                break;
            case "5":
                // 一年内
                uploadDate = "" + DateUtil.offsetDay(new Date(), -365);
                break;
            default:
                // 默认 全部
                uploadDate = "";
                break;
        }
        if (StringUtils.isNotEmpty(uploadDate)) {
            boolQuery.filter(QueryBuilders.rangeQuery("uploadTime").gte(uploadDate).lte(System.currentTimeMillis() + ""));
        }
        // duration: '',
        String duration = searchMap.get("duration");
        String startTime;
        if (StringUtils.isEmpty(duration)) {
            duration = "";
        }
        switch (duration) {
            case "2":
                // 十分钟以下
                duration = "600";
                startTime = "0";
                break;
            case "3":
                // 10-30
                duration = "1800";
                startTime = "600";
                break;
            case "4":
                // 30-60
                duration = "3600";
                startTime = "1800";
                break;
            case "5":
                // 大于60
                duration = "9999999";
                startTime = "3600";
                break;
            default:
                // 默认 全部
                duration = "";
                startTime = "0";
                break;
        }
        if (StringUtils.isNotEmpty(duration)) {
            boolQuery.filter(QueryBuilders.rangeQuery("durations").gte(startTime).lte(duration));
        }


        //开启分页查询 当前页
        String pageNum = searchMap.get("pageNum");
        //每页显示多少条
        String pageSize = searchMap.get("pageSize");
        //设置分页
        //第一个参数:当前页 是从0开始
        //第二个参数:每页显示多少条
        nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum) - 1, Integer.parseInt(pageSize)));

        //设置高亮域以及高亮的样式
        //高亮域
        HighlightBuilder.Field field = new HighlightBuilder.Field("title")
                //高亮样式的前缀
                .preTags("<span style='color:red'>")
                //高亮样式的后缀
                .postTags("</span>");
        nativeSearchQueryBuilder.withHighlightFields(field);

        AggregatedPage<VideoInfo> resultInfo = elasticsearchRestTemplate.queryForPage(nativeSearchQueryBuilder.build(),
                VideoInfo.class, new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                        List<T> videoInfos = new ArrayList<>();
                        SearchHits searchHits = response.getHits();
                        if (searchHits != null) {
                            for (SearchHit searchHit : searchHits) {
                                //SearchHit转换为videoInfo
                                VideoInfo videoInfo = JSON.parseObject(searchHit.getSourceAsString(), VideoInfo.class);

                                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                                if (highlightFields != null && highlightFields.size() > 0) {
                                    //替换数据
                                    videoInfo.setTitle(highlightFields.get("title").getFragments()[0].toString());
                                }

                                videoInfos.add((T) videoInfo);
                            }
                        }
                        // 封装数据
                        return new AggregatedPageImpl<>(
                                videoInfos,
                                pageable,
                                searchHits == null ? 0 : searchHits.getTotalHits().value,
                                response.getAggregations());
                    }

                    @Override
                    public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
                        return null;
                    }
                });

        //封装最终的返回结果

        //总记录数
        resultMap.put("total", resultInfo.getTotalElements());
        //总页数
        resultMap.put("totalPages", resultInfo.getTotalPages());
        //数据集合
        resultMap.put("rows", resultInfo.getContent());

        //当前页
        resultMap.put("pageNum", pageNum);
        return resultMap;
    }
}
